fix(ci): add least-privilege permissions and stop the push sync clobbering releases - #405
Merged
Merged
Conversation
This repo's default workflow permission is "read", which grants only contents/metadata/packages. `gh pr view --json comments` needs pull-requests, so those lookups were failing silently. Declare per-job scopes explicitly. An explicit permissions block is subtractive -- anything not listed becomes none -- so contents: read is re-declared for actions/checkout and for linear-release-action's github_token, which it uses only to download the Linear release CLI. delete-linear-release talks only to api.linear.app and needs no token at all. Also record why INGESTION_LINEAR_KEY must exist in this repo: the workflows that share it live upstream in datahub-project/datahub, where it is configured, and secrets do not cross the fork boundary. When it is missing GitHub substitutes an empty string, so every Linear call 401s silently and releases are created with zero issues attached.
linear-release-action's push sync is the pattern for CONTINUOUS pipelines, where each push becomes its own release. This pipeline is `scheduled`, so a versionless sync creates nothing -- it targets the most recent release, rewrites its commitSha to master HEAD and attaches commits published after it. That is not hypothetical. On 2026-08-11 a push run rewrote v1.7.0.2's commitSha from f579f9c (its tag) to master HEAD and attached 17 PRs that shipped after the release. The next ingestion push would do the same to v1.7.0.3. The behaviour was dormant for three months only because the owner/repo- qualified path filters made every push match zero commits and skip. Fixing those filters reactivated it. Drop the push trigger and the step. Staging work on merge is already the add-to-cli-next job's responsibility, via pull_request_target, which writes to the cli-next release rather than to a versioned one.
sgomezvillamor
approved these changes
Aug 31, 2026
The release lookup piped the response straight into `jq -r '... // empty'`, so an auth or scope failure -- which returns errors[] with data:null -- produced the same "Could not find Linear release" message as a genuinely absent release. A broken credential and a missing release were indistinguishable in the log. Check for errors[] and print them, failing the step. Everything else about the flow is unchanged.
ReleasePipeline.releases takes no `filter` argument. The query returned Unknown argument "filter" on field "ReleasePipeline.releases" GRAPHQL_VALIDATION_FAILED (400) so the lookup has never succeeded -- not once since the workflow was written. Both call sites were affected: the attach step in linear-release, and the lookup in delete-linear-release, meaning release deletion has never worked either. Fetch the pipeline's releases and match the version client-side instead. Verified against the live API: the corrected query returns the expected ids for v1.7.0.8rc1 and v1.7.0.2. This was the third fault stacked on the same step, behind the missing INGESTION_LINEAR_KEY secret and the missing pull-requests scope. All three degraded to the same "Could not find Linear release" line and exit 0, which is why fixing the first two changed nothing visible.
5 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Two independent bugs in the Linear release workflow, both of which fail silently.
1. The jobs had no GitHub permissions they needed
This repo's
default_workflow_permissionsisread, which grants only contents / metadata / packages.pull-requestsisnone, so everygh pr view --json commentslookup — the PR-to-ticket resolution in the attach step, and the issue lookup inadd-to-cli-next— was failing.Per-job scopes are now declared explicitly, least privilege:
add-to-cli-nextcontents: read,pull-requests: readgh pr view --json commentslinear-releasecontents: read,pull-requests: readactions/checkout, the action's CLI download, and the attach step's PR lookupsdelete-linear-release{}api.linear.appNote an explicit
permissions:block is subtractive — anything not listed becomesnone— socontents: readhas to be re-declared oractions/checkoutbreaks. No job needs any write scope on GitHub: the workflow reads GitHub and writes only to Linear.2. The push sync dumps post-release commits into a versioned release
linear-release-action's push sync is the pattern for continuous pipelines, where each push becomes its own release. This pipeline isscheduled, so a versionless sync creates nothing — it targets the most recent release, rewrites itscommitShato master HEAD, and attaches commits published after it.Not hypothetical. On 2026-08-11 a push run did this:
The target is not "the newest release" — it is whichever release sits in the pipeline's initial
In Progressstage, the accumulator slot.v1.7.0.2has occupied that slot since it was created and never completed, so it has absorbed every ingestion push since 2026-08-11. ItscommitShahas been walked forward fromf579f9c4(its tag) to master HEAD, currently7461cd25, and it now carries a long tail of PRs that shipped in later releases.Properly published releases are unaffected —
v1.7.0.5throughv1.7.0.8rc1all still carry their own tag's commit — so the damage is confined to one release acting as a junk drawer. But the accumulator for this pipeline is meant to be thecli-nextplaceholder, whichadd-to-cli-nextwrites to; the action has no knowledge of it and picks a versioned release instead.The behaviour was dormant for three months only because the owner/repo-qualified path filters made every push match zero commits and skip. Fixing those filters in #403 reactivated it — so this is a regression introduced by that fix.
The
pushtrigger and its step are removed. Staging on merge is alreadyadd-to-cli-next's job, viapull_request_target, writing to thecli-nextrelease rather than to a versioned one.Not fixed here
INGESTION_LINEAR_KEYdoes not exist as a secret in this repo. The workflows that share it (pr-to-linear.yml,linear-assignment-notify.yml) live upstream indatahub-project/datahub, where it is configured, and secrets do not cross the fork boundary. When it is missing GitHub substitutes an empty string, so every direct Linear GraphQL call 401s and the step exits 0 — releases get created with zero issues attached.That needs a repo (or scoped org) secret containing a Linear personal API key with Read + Write, and access to all teams whose issues can appear in a release — the five tickets resolved for
v1.7.0.2span four (Ingestion Pod, Observability Squad, Open Source, Catalog). A key limited to one team would return null for the rest and skip them silently.LINEAR_ACCESS_KEYis already present but is not a substitute: it is a release-pipeline access key for thelinear-releaseCLI and cannot authorizeissueToReleaseCreate,issueToReleaseDeleteByIssueAndRelease,releaseDelete, or issue lookups.Once the secret exists,
v1.7.0.2needs itscommitShareset tof579f9c4and a re-run ofsyncto attach its issues. Doing that before this PR merges would simply be overwritten by the next push.Checklist
actionlintandgithubActionsPrettierCheckpass🤖 Generated with Claude Code